package com.lxw.multidb.modules.es;

import com.lxw.multidb.controller.es.ImportProgressDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * 进度缓存管理器
 * 负责ES数据导入任务的进度缓存管理和延迟清理功能
 * <p>
 * 主要功能：
 * 1. 存储和查询任务进度信息
 * 2. 延迟清理缓存，确保前端能获取到最终状态（包括成功和异常状态）
 * 3. 线程安全的缓存操作
 * <p>
 * 设计说明：
 * - 使用ConcurrentHashMap保证线程安全
 * - 支持延迟删除，给前端轮询留出足够时间
 * - 默认延迟5秒清理，可配置
 * - 异常情况也延迟删除，确保前端能获取到异常信息
 *
 * @author lxw
 * @date 2025-10-31
 */
@Component
public class ProgressCacheManager {

    private static final Logger log = LoggerFactory.getLogger(ProgressCacheManager.class);

    /**
     * 任务进度缓存
     * key: 批次号（batchNo）
     * value: 任务进度信息（ImportProgressDTO）
     * <p>
     * 使用ConcurrentHashMap保证多线程环境下的安全访问
     */
    private final Map<String, ImportProgressDTO> progressCache = new ConcurrentHashMap<>();

    /**
     * 默认延迟清理时间（秒）
     * 设置为5秒，确保前端轮询（每5秒一次）能获取到最后一次进度结果
     */
    private static final int DEFAULT_DELAY_SECONDS = 5;

    /**
     * 更新任务进度
     * 将最新的进度信息存入缓存
     *
     * @param batchNo  批次号
     * @param progress 进度信息
     * @author lxw
     * @date 2025-10-31
     */
    public void updateProgress(String batchNo, ImportProgressDTO progress) {
        progressCache.put(batchNo, progress);
        log.info("===> 进度缓存更新，批次号：{}，进度：{}/{}，状态：{}，消息：{}",
                batchNo, progress.getFinishCount(), progress.getTotalCount(),
                progress.getState(), progress.getMsg());
    }

    /**
     * 获取任务进度
     * 从缓存中查询指定批次的进度信息
     * <p>
     * 使用场景：
     * - 前端页面轮询调用，实时展示导入进度
     * - 任务完成后缓存会被延迟清理，期间仍可查询到完成状态
     * - 任务异常后缓存也会延迟清理，期间仍可查询到异常信息
     *
     * @param batchNo 批次号
     * @return ImportProgressDTO 任务进度信息，如果未找到则返回默认已完成状态
     * @author lxw
     * @date 2025-10-31
     */
    public ImportProgressDTO getProgress(String batchNo) {
        // 从缓存中获取进度
        ImportProgressDTO progress = progressCache.get(batchNo);

        // 如果未获取到（任务已完成清理或不存在），返回默认完成状态
        if (progress == null) {
            log.info("===> 进度缓存未找到, 批次号：{}，返回默认完成状态", batchNo);
            return ImportProgressDTO.defaultCompleted();
        }

        return progress;
    }

    /**
     * 延迟删除进度缓存（使用默认延迟时间）
     * 在指定时间后异步删除进度缓存，确保前端轮询能获取到最终状态
     * <p>
     * 使用场景：
     * - 任务正常完成
     * - 任务异常终止
     * - 需要给前端留出时间获取最终进度（无论成功还是失败）
     * <p>
     * 技术实现：
     * - 使用@Async异步执行，不阻塞主线程
     * - 默认延迟5秒，与前端轮询间隔匹配
     *
     * @param batchNo 批次号
     * @author lxw
     * @date 2025-10-31
     */
    @Async("esImportExecutor")
    public void removeWithDelay(String batchNo) {
        removeWithDelay(batchNo, DEFAULT_DELAY_SECONDS);
    }

    /**
     * 延迟删除进度缓存（自定义延迟时间）
     * 在指定时间后异步删除进度缓存
     * <p>
     * 工作原理：
     * 1. 异步线程睡眠指定秒数
     * 2. 睡眠结束后删除缓存
     * 3. 记录删除日志
     * <p>
     * 注意事项：
     * - 方法使用@Async异步执行，不会阻塞调用线程
     * - 如果线程被中断，会记录警告日志并立即删除缓存
     *
     * @param batchNo      批次号
     * @param delaySeconds 延迟秒数
     * @author lxw
     * @date 2025-10-31
     */
    @Async("esImportExecutor")
    public void removeWithDelay(String batchNo, int delaySeconds) {
        try {
            log.info("===> 进度缓存将在{}秒后删除，批次号：{}", delaySeconds, batchNo);

            // 线程休眠指定时间，等待前端获取最后一次进度
            TimeUnit.SECONDS.sleep(delaySeconds);

            // 延迟时间到，删除缓存
            progressCache.remove(batchNo);
            log.info("===> 进度缓存延迟删除完成，批次号：{}", batchNo);

        } catch (InterruptedException e) {
            // 线程被中断，记录警告并恢复中断状态
            log.warn("===> 进度缓存延迟删除被中断，立即删除缓存，批次号：{}", batchNo);
            Thread.currentThread().interrupt();
            // 被中断时立即删除缓存
            progressCache.remove(batchNo);
        }
    }

    /**
     * 标记任务为完成状态
     * 更新进度为完成状态，并启动延迟清理
     * <p>
     * 使用场景：
     * - 任务正常完成时调用
     * - 自动更新进度为完成状态
     * - 自动启动延迟清理流程
     *
     * @param batchNo     批次号
     * @param totalCount  总条数
     * @param finishCount 已完成条数
     * @author lxw
     * @date 2025-10-31
     */
    public void markAsCompleted(String batchNo, Long totalCount, Long finishCount) {
        // 更新进度为完成状态，使用实际的总量和完成量
        ImportProgressDTO completedProgress = ImportProgressDTO.completed(totalCount, finishCount);
        progressCache.put(batchNo, completedProgress);
        log.info("===> 进度缓存标记为完成，批次号：{}，总量：{}，完成量：{}", batchNo, totalCount, finishCount);

        // 启动延迟清理
        removeWithDelay(batchNo);
    }

    /**
     * 标记任务为完成状态（带自定义消息）
     * 更新进度为完成状态，并启动延迟清理
     * <p>
     * 使用场景：
     * - 任务正常完成时调用
     * - 需要自定义完成消息
     * - 自动启动延迟清理流程
     *
     * @param batchNo     批次号
     * @param totalCount  总条数
     * @param finishCount 已完成条数
     * @param msg         自定义消息
     * @author lxw
     * @date 2025-10-31
     */
    public void markAsCompleted(String batchNo, Long totalCount, Long finishCount, String msg) {
        // 更新进度为完成状态，使用实际的总量和完成量及自定义消息
        ImportProgressDTO completedProgress = ImportProgressDTO.completed(totalCount, finishCount, msg);
        progressCache.put(batchNo, completedProgress);
        log.info("===> 进度缓存标记为完成，批次号：{}，总量：{}，完成量：{}，消息：{}", batchNo, totalCount, finishCount, msg);

        // 启动延迟清理
        removeWithDelay(batchNo);
    }

    /**
     * 标记任务为失败状态
     * 更新进度为失败状态，并启动延迟清理
     * <p>
     * 使用场景：
     * - 任务执行异常时调用
     * - 自动更新进度为失败状态
     * - 自动启动延迟清理流程（与成功一样延迟删除，确保前端能获取异常信息）
     *
     * @param batchNo     批次号
     * @param totalCount  总条数
     * @param finishCount 已完成条数
     * @param msg         异常消息说明
     * @author lxw
     * @date 2025-10-31
     */
    public void markAsFailed(String batchNo, Long totalCount, Long finishCount, String msg) {
        // 更新进度为失败状态，记录异常信息
        ImportProgressDTO failedProgress = ImportProgressDTO.failed(totalCount, finishCount, msg);
        progressCache.put(batchNo, failedProgress);
        log.error("===> 进度缓存标记为失败，批次号：{}，总量：{}，完成量：{}，异常：{}", batchNo, totalCount, finishCount, msg);

        // 启动延迟清理（异常情况也延迟删除，确保前端能获取到异常信息）
        removeWithDelay(batchNo);
    }

    /**
     * 获取当前缓存中的任务数量
     * 用于监控和调试
     *
     * @return int 当前缓存的任务数量
     * @author lxw
     * @date 2025-10-31
     */
    public int getCacheSize() {
        return progressCache.size();
    }

    /**
     * 清空所有进度缓存
     * 用于系统维护或重置场景
     * <p>
     * 注意：此操作会清空所有任务的进度信息，谨慎使用
     *
     * @author lxw
     * @date 2025-10-31
     */
    public void clearAll() {
        int size = progressCache.size();
        progressCache.clear();
        log.info("===> 进度缓存清空所有，共清理{}个任务", size);
    }
}